﻿using System;
using OpenTK;

namespace AnimatingHair
{
    static class Const
    {
        // TODO : magic numbers!

        public static long Time = 0;
        public const double TimeStep = 0.2;

        public static int Seed = 0;
        public static int ParticleCount = 125;
        public static double HairLength = 2;
        public static double s_r = 0.5; // hlbka do ktorej maximalne idu root particles
        public static double DensityOfHairMaterial = 1;
        public static double DefaultParticleMass = 1; // TODO : make dependent on particlecount and hairlength

        public static double AirFriction = 0.1;
        public static double Gravity = 0.01;

        public static double E = 10; // elastic modulus - zodpoveda materialu
        public static double d_c = 0.9; // collision damping
        public static double d_f = 0.9; // frictional damping
        public static double k_a = 0.01; // magnitude of attraction-repulsion forces - pre udrziavanie density
        public static double rho_0 = 5; // average density of hair

        public static int N_n = 12; // max neighbor particles
        public static double a_0 = 0.5; // treshold pre slabo spojene pary

        public static void SaveConfiguration( string path )
        {
            System.IO.StreamWriter streamWriter = new System.IO.StreamWriter( path );

            streamWriter.WriteLine( Seed );
            streamWriter.WriteLine( ParticleCount );
            streamWriter.WriteLine( HairLength );
            streamWriter.WriteLine( s_r );
            streamWriter.WriteLine( DensityOfHairMaterial );
            streamWriter.WriteLine( DefaultParticleMass );

            streamWriter.WriteLine( AirFriction );
            streamWriter.WriteLine( Gravity );

            streamWriter.WriteLine( E );
            streamWriter.WriteLine( d_c );
            streamWriter.WriteLine( d_f );
            streamWriter.WriteLine( k_a );
            streamWriter.WriteLine( rho_0 );

            streamWriter.Close();
        }

        public static void LoadConfiguration( string path )
        {
            System.IO.StreamReader streamReader = new System.IO.StreamReader( path );

            Seed = int.Parse( streamReader.ReadLine() );
            ParticleCount = int.Parse( streamReader.ReadLine() );

            HairLength = double.Parse( streamReader.ReadLine() );
            s_r = double.Parse( streamReader.ReadLine() );
            DensityOfHairMaterial = double.Parse( streamReader.ReadLine() );
            DefaultParticleMass = double.Parse( streamReader.ReadLine() );

            AirFriction = double.Parse( streamReader.ReadLine() );
            Gravity = double.Parse( streamReader.ReadLine() );

            E = double.Parse( streamReader.ReadLine() );
            d_c = double.Parse( streamReader.ReadLine() );
            d_f = double.Parse( streamReader.ReadLine() );
            k_a = double.Parse( streamReader.ReadLine() );
            rho_0 = double.Parse( streamReader.ReadLine() );

            streamReader.Close();
        }

        public static double W( double r, double h )
        {
            if ( r >= 0 && r <= h )
            {
                double factor = (1 / (4 * Math.PI * h * h * h));
                double tmp = r / h;
                return factor * (4 + 3 * tmp * tmp * (tmp - 2));
                //return factor * (4 - 6 * tmp * tmp + 3 * tmp * tmp * tmp);
            }

            if ( r > h && r <= 2 * h )
            {
                double factor = (1 / (4 * Math.PI * h * h * h));
                double tmp = 2 - (r / h);
                return factor * tmp * tmp * tmp;
            }

            return 0;
        }

        private static double WdiffA( double i, double j, double sq, double h )
        {
            double factor = (1 / (4 * Math.PI * h * h * h));
            return factor * (-(3 * (j - i) * (3 * sq - 4 * h)) / (h * h * h));
        }

        private static double WdiffB( double i, double j, double sq, double h )
        {
            double factor = (1 / (4 * Math.PI * h * h * h));
            return factor * ((3 * (j - i) * (sq - 2 * h) * (sq - 2 * h)) / (h * h * h * sq));
        }

        public static Vector3d Wgrad( Vector3d i, Vector3d j, double h )
        {
            double r = (i - j).Length;
            double sq = Math.Sqrt( (i.X - j.X) * (i.X - j.X) + (i.Y - j.Y) * (i.Y - j.Y) + (i.Z - j.Z) * (i.Z - j.Z) );

            Vector3d result = Vector3d.Zero;

            if ( r >= 0 && r <= h )
            {
                result.X = WdiffA( i.X, j.X, sq, h );
                result.Y = WdiffA( i.Y, j.Y, sq, h );
                result.Z = WdiffA( i.Z, j.Z, sq, h );
            }

            if ( r > h && r <= 2 * h )
            {
                result.X = WdiffB( i.X, j.X, sq, h );
                result.Y = WdiffB( i.Y, j.Y, sq, h );
                result.Z = WdiffB( i.Z, j.Z, sq, h );
            }

            return result;
        }

        #region For testing purposes

        private static double Wdiff2A( double i, double j, double sq, double h )
        {
            double factor = (1 / (4 * Math.PI * h * h * h));
            return factor * (-(3 * (i - j) * (4 * h - 3 * i + 3 * j)) / (h * h * h));
        }

        private static double Wdiff2B( double i, double j, double sq, double h )
        {
            double factor = (1 / (4 * Math.PI * h * h * h));
            return factor * (-(3 * (2 * h - i + j) * (2 * h - i + j)) / (h * h * h));
        }

        public static Vector3d Wgrad2( Vector3d i, Vector3d j, double h )
        {
            double r = (i - j).Length;
            double sq = Math.Sqrt( (i.X - j.X) * (i.X - j.X) + (i.Y - j.Y) * (i.Y - j.Y) + (i.Z - j.Z) * (i.Z - j.Z) );

            Vector3d result = Vector3d.Zero;

            if ( r >= 0 && r <= h )
            {
                result.X = Wdiff2A( i.X, j.X, sq, h );
                result.Y = Wdiff2A( i.Y, j.Y, sq, h );
                result.Z = Wdiff2A( i.Z, j.Z, sq, h );
            }

            if ( r > h && r <= 2 * h )
            {
                result.X = Wdiff2B( i.X, j.X, sq, h );
                result.Y = Wdiff2B( i.Y, j.Y, sq, h );
                result.Z = Wdiff2B( i.Z, j.Z, sq, h );
            }

            return result;
        }

        #endregion
    }
}
